home *** CD-ROM | disk | FTP | other *** search
- >From turk@apple.UUCP Wed Feb 17 21:19:06 1988
- Path: leah!itsgw!nysernic!rutgers!ucla-cs!cit-vax!elroy!ames!pasteur!ucbvax!hplabs!nsc!voder!apple!turk
- From: turk@apple.UUCP (Ken "Turk" Turkowski)
- Newsgroups: comp.graphics
- Subject: Re: CORDICS: Is Ken Turkowski there? (was: Fractals...Jim Cathey where are you?)
- Summary: CORDIC derivation and code enclosed
- Keywords: CORDIC, fixed-point trigonometric functions
- Message-ID: <7433@apple.UUCP>
- Date: 18 Feb 88 02:19:06 GMT
- References: <6019@iuvax.UUCP> <2479@orca.TEK.COM>
- Reply-To: turk@apple.UUCP (Ken "Turk" Turkowski)
- Organization: Advanced Technology Graphics, Apple Computer, Cupertino, CA, USA
- Lines: 373
-
- In article <2479@orca.TEK.COM> brucec@orca.UUCP (Bruce Cohen) writes:
- >In article <6019@iuvax.UUCP> viking@iuvax writes:
- >>
- >>The major enhancements that Jim added to the program were to recode it in
- >>C and to use "a CORDIC rotator instead of trig functions". I want to talk
- >>with Jim about his program....where is he?!
- >>
- >>Alternately, does anyone know anything about "CORDIC rotators"? I'd like to
- >>see the source to his modification of Michiel's program. The algorithm used
- >>was based on one developed by LucasFilm Ltd. and published in the Sept. '84
- >>Scientific American.
-
- The idea is really simple. A rotation can be expressed as:
-
- [ x' ] [ cos t -sin y ] [ x ]
- [ y' ] = [ sin t cos t ] [ y ]
-
- Factor out cos t, giving:
-
- [ x' ] [ 1 -tan y ] [ x ]
- [ y' ] = cos t [ tan t 1 ] [ y ]
-
- Now let t be expressed as a signed sum of
- -1 -i
- t = Sum { d tan 2 }, where d = {+1, -1}
- i i i
-
- This yields (please forgive the cruddy typesetting; Mathtype doesn't work
- on UNIX):
-
- -i
- [ x' ] [ 1 -d 2 ] [ x ]
- [ ] [ i ] [ ]
- [ ] = Sum { C } Sum { [ -i ] [ ] }
- [ y' ] i i i [ d 2 1 ] [ y ]
- i
-
- The first sum is a constant. The second sum is a series of shifts and
- adds (or subtracts).
-
-
- This is illustrated by the following working code, which performs all of the
- following operations:
- rotate a vector
- polar to rectangular conversion
- rectangular to polar conversion
- simultaneous calculation of sine and cosine.
- atan2()
- Note that the normalization and denormalization is done mainly to increase
- accuracy; if you're more concerned about speed, you'd probably hack this
- out entirely or just shift by a fixed amount.
-
- You can do similar sorts of things with hyperbolic functions, thereby
- being able to do exponential and logarithms. I have never written such
- code, although I would like to have it, especially to calculate gamma
- correction tables.
-
- ------------------------------------------------------------
-
- # define RADIANS 1
- # define COSCALE 0x22c2dd1c /* 0.271572 */
-
- static long arctantab[32] = {
- # ifdef DEGREES /* MS 10 integral bits */
- # define QUARTER (90 << 22)
- 266065460, 188743680, 111421900, 58872272, 29884485, 15000234, 7507429,
- 3754631, 1877430, 938729, 469366, 234683, 117342, 58671, 29335, 14668,
- 7334, 3667, 1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1, 0, 0,
- # else
- # ifdef RADIANS /* MS 4 integral bits */
- # define QUARTER ((int)(3.141592654 / 2.0 * (1 << 28)))
- 297197971, 210828714, 124459457, 65760959, 33381290, 16755422, 8385879,
- 4193963, 2097109, 1048571, 524287, 262144, 131072, 65536, 32768, 16384,
- 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0, 0,
- # else
- # define BRADS 1
- # define QUARTER (1 << 30)
- 756808418, 536870912, 316933406, 167458907, 85004756, 42667331,
- 21354465, 10679838, 5340245, 2670163, 1335087, 667544, 333772, 166886,
- 83443, 41722, 20861, 10430, 5215, 2608, 1304, 652, 326, 163, 81, 41,
- 20, 10, 5, 3, 1, 1,
- # endif
- # endif
- };
-
-
- /* To rotate a vector through an angle of theta, we calculate:
- *
- * x' = x cos(theta) - y sin(theta)
- * y' = x sin(theta) + y cos(theta)
- *
- * The rotate() routine performs multiple rotations of the form:
- *
- * x[i+1] = cos(theta[i]) { x[i] - y[i] tan(theta[i]) }
- * y[i+1] = cos(theta[i]) { y[i] + x[i] tan(theta[i]) }
- *
- * with the constraint that tan(theta[i]) = pow(2, -i), which can be
- * implemented by shifting. We always shift by either a positive or
- * negative angle, so the convergence has the ringing property. Since the
- * cosine is always positive for positive and negative angles between -90
- * and 90 degrees, a predictable magnitude scaling occurs at each step,
- * and can be compensated for instead at the end of the iterations by a
- * composite scaling of the product of all the cos(theta[i])'s.
- */
-
- static
- pseudorotate(px, py, theta)
- long *px, *py;
- register long theta;
- {
- register int i;
- register long x, y, xtemp;
- register long *arctanptr;
-
- x = *px;
- y = *py;
-
- /* Get angle between -90 and 90 degrees */
- while (theta < -QUARTER) {
- x = -x;
- y = -y;
- theta += 2 * QUARTER;
- }
- while (theta > QUARTER) {
- x = -x;
- y = -y;
- theta -= 2 * QUARTER;
- }
-
- /* Initial pseudorotation, with left shift */
- arctanptr = arctantab;
- if (theta < 0) {
- xtemp = x + (y << 1);
- y = y - (x << 1);
- x = xtemp;
- theta += *arctanptr++;
- }
- else {
- xtemp = x - (y << 1);
- y = y + (x << 1);
- x = xtemp;
- theta -= *arctanptr++;
- }
-
- /* Subsequent pseudorotations, with right shifts */
- for (i = 0; i <= 28; i++) {
- if (theta < 0) {
- xtemp = x + (y >> i);
- y = y - (x >> i);
- x = xtemp;
- theta += *arctanptr++;
- }
- else {
- xtemp = x - (y >> i);
- y = y + (x >> i);
- x = xtemp;
- theta -= *arctanptr++;
- }
- }
-
- *px = x;
- *py = y;
- }
-
-
- static
- pseudopolarize(argx, argy)
- long *argx, *argy;
- {
- register long theta;
- register long yi, i;
- register long x, y;
- register long *arctanptr;
-
- x = *argx;
- y = *argy;
-
- /* Get the vector into the right half plane */
- theta = 0;
- if (x < 0) {
- x = -x;
- y = -y;
- theta = 2 * QUARTER;
- }
-
- if (y > 0)
- theta = - theta;
-
- arctanptr = arctantab;
- if (y < 0) { /* Rotate positive */
- yi = y + (x << 1);
- x = x - (y << 1);
- y = yi;
- theta -= *arctanptr++; /* Subtract angle */
- }
- else { /* Rotate negative */
- yi = y - (x << 1);
- x = x + (y << 1);
- y = yi;
- theta += *arctanptr++; /* Add angle */
- }
-
- for (i = 0; i <= 28; i++) {
- if (y < 0) { /* Rotate positive */
- yi = y + (x >> i);
- x = x - (y >> i);
- y = yi;
- theta -= *arctanptr++;
- }
- else { /* Rotate negative */
- yi = y - (x >> i);
- x = x + (y >> i);
- y = yi;
- theta += *arctanptr++;
- }
- }
-
- *argx = x;
- *argy = theta;
- }
-
-
- /* Fxprenorm() block normalizes the arguments to a magnitude suitable for
- * CORDIC pseudorotations. The returned value is the block exponent.
- */
- static int
- fxprenorm(argx, argy)
- long *argx, *argy;
- {
- register long x, y;
- register int shiftexp;
- int signx, signy;
-
- shiftexp = 0; /* Block normalization exponent */
- signx = signy = 1;
-
- if ((x = *argx) < 0) {
- x = -x; signx = -signx;
- }
- if ((y = *argy) < 0) {
- y = -y; signy = -signy;
- }
- /* Prenormalize vector for maximum precision */
- if (x < y) { /* |y| > |x| */
- while (y < (1 << 27)) {
- x <<= 1; y <<= 1; shiftexp--;
- }
- while (y > (1 << 28)) {
- x >>= 1; y >>= 1; shiftexp++;
- }
- }
- else { /* |x| > |y| */
- while (x < (1 << 27)) {
- x <<= 1; y <<= 1; shiftexp--;
- }
- while (x > (1 << 28)) {
- x >>= 1; y >>= 1; shiftexp++;
- }
- }
-
- *argx = (signx < 0) ? -x : x;
- *argy = (signy < 0) ? -y : y;
- return(shiftexp);
- }
-
-
- /* Return a unit vector corresponding to theta.
- * sin and cos are fixed-point fractions.
- */
- fxunitvec(cos, sin, theta)
- long *cos, *sin, theta;
- {
- *cos = COSCALE >> 1; /* Save a place for the integer bit */
- *sin = 0;
- pseudorotate(cos, sin, theta);
-
- /* Saturate results to fractions less than 1, shift out integer bit */
- if (*cos >= (1 << 30))
- *cos = 0x7FFFFFFF; /* Just shy of 1 */
- else if (*cos <= -(1 << 30))
- *cos = 0x80000001; /* Just shy of -1*/
- else
- *cos <<= 1;
-
- if (*sin >= (1 << 30))
- *sin = 0x7FFFFFFF; /* Just shy of 1 */
- else if (*sin <= -(1 << 30))
- *sin = 0x80000001; /* Just shy of -1*/
- else
- *sin <<= 1;
- }
-
-
- /* Fxrotate() rotates the vector (argx, argy) by the angle theta. */
- fxrotate(argx, argy, theta)
- long *argx, *argy;
- long theta;
- {
- register long x, y;
- int shiftexp;
- long frmul();
-
- if (((*argx || *argy) == 0) || (theta == 0))
- return;
-
- shiftexp = fxprenorm(argx, argy); /* Prenormalize vector */
- pseudorotate(argx, argy, theta); /* Perform CORDIC pseudorotation */
- x = frmul(*argx, COSCALE); /* Compensate for CORDIC enlargement */
- y = frmul(*argy, COSCALE);
- if (shiftexp < 0) { /* Denormalize vector */
- *argx = x >> -shiftexp;
- *argy = y >> -shiftexp;
- }
- else {
- *argx = x << shiftexp;
- *argy = y << shiftexp;
- }
- }
-
-
- fxatan2(x, y)
- long x, y;
- {
- if ((x || y) == 0)
- return(0);
- fxprenorm(&x, &y); /* Prenormalize vector for maximum precision */
- pseudopolarize(&x, &y); /* Convert to polar coordinates */
- return(y);
- }
-
-
- fxpolarize(argx, argy)
- long *argx, *argy;
- {
- int shiftexp;
- long frmul();
-
- if ((*argx || *argy) == 0) {
- *argx = 0; /* Radius */
- *argy = 0; /* Angle */
- return;
- }
-
- /* Prenormalize vector for maximum precision */
- shiftexp = fxprenorm(argx, argy);
- /* Perform CORDIC conversion to polar coordinates */
- pseudopolarize(argx, argy);
- /* Scale radius to undo pseudorotation enlargement factor */
- *argx = frmul(*argx, COSCALE);
- /* Denormalize radius */
- *argx = (shiftexp < 0) ? (*argx >> -shiftexp) : (*argx << shiftexp);
- }
-
-
- # ifdef vax
- long
- frmul(a, b) /* Just for testing */
- long a, b;
- {
- /* This routine should be written in assembler to calculate the
- * high part of the product, i.e. the product when the operands
- * are considered fractions.
- */
- return((a >> 16) * (b >> 15));
- }
- # endif
-
- ##### This is the end of my posting #####
- --
- Ken Turkowski @ Apple Computer, Inc., Cupertino, CA
- UUCP: {mtxinu,sun,decwrl,nsc,voder}!apple!turk
- CSNET: turk@Apple.CSNET
- ARPA: turk%Apple@csnet-relay.ARPA
-
-
-